/*
 * bsp_iadc.c
 *
 *  Created on: 2023年8月1日
 *      Author: zlw
 */
#include "bsp_iadc.h"
#include "app_log.h"
#include "device_type.h"
#include "softwareMultiTimer.h"
#include "em_letimer.h"

// Set CLK_ADC to 10MHz
#define CLK_SRC_ADC_FREQ      1000000  // CLK_SRC_ADC
#define CLK_ADC_FREQ          10000  // CLK_ADC - 10 MHz max in normal mode
#ifdef SM_PIR
#define REFERENCE_VOLTAGE      3300
#else
#define REFERENCE_VOLTAGE      1210
#endif


/*************************RFM device iadc init********************************/
/**
 * @brief:初始化iadc
 * */
static void ds_battery_monitor_init(IADC_PosInput_t input)
{
  CMU_ClockEnable(cmuClock_IADC0, true);
  // Select clock for IADC
  CMU_ClockSelectSet(cmuClock_IADCCLK, cmuSelect_FSRCO);

  IADC_Init_t init = IADC_INIT_DEFAULT;
  IADC_AllConfigs_t initAllConfigs = IADC_ALLCONFIGS_DEFAULT;
  IADC_InitSingle_t initSingle = IADC_INITSINGLE_DEFAULT;
  IADC_SingleInput_t initSingleInput = IADC_SINGLEINPUT_DEFAULT;

  init.warmup = iadcWarmupKeepWarm;
  init.srcClkPrescale = IADC_calcSrcClkPrescale(IADC0, CLK_SRC_ADC_FREQ, 0);

  IADC_reset(IADC0);
  initAllConfigs.configs[0].reference = iadcCfgReferenceInt1V2;
  initAllConfigs.configs[0].adcClkPrescale = IADC_calcAdcClkPrescale(IADC0,
                                                                       CLK_ADC_FREQ,
                                                                       0,
                                                                       iadcCfgModeNormal,
                                                                       init.srcClkPrescale);
  initSingleInput.posInput = input;
  initSingleInput.negInput = iadcNegInputGnd;

#if defined(SM_KP) || defined(SM_SOUND) || defined(SM_OUTSOUND) || defined(SM_MC) || defined(SM_TEMPHUMI)
  GPIO->CDBUSALLOC |= GPIO_CDBUSALLOC_CDEVEN0_ADC0;
#endif
  IADC_init(IADC0, &init, &initAllConfigs);
  IADC_initSingle(IADC0, &initSingle, &initSingleInput);
}

static uint32_t AdcToMilliV(uint32_t adcVal)
{
  uint32_t vMax = 0xFFF; // 12 bit ADC maximum
  uint32_t referenceMilliV = 1210;
  float milliVPerBit = (float)referenceMilliV / (float)vMax;

  return (uint32_t)(milliVPerBit * adcVal);
}

/**
 * @brief:获取电压值
 * @return: 返回电压值，unit：mv
 * */
uint32_t vData[8] = {0x00};
void battery_detect_start(IADC_PosInput_t input)
{
  uint8_t i=0;
  IADC_Result_t sample = {0};
  uint32_t flags = 0;
  uint32_t ticks = 0;

  ds_battery_monitor_init(input);
  memset(vData,0,sizeof(vData));

  for(i = 0; i < 8; i++){
      IADC_command(IADC0, iadcCmdStartSingle);
      flags = IADC_getInt(IADC0);
      IADC_clearInt(IADC0, flags);
      // wait for the ADC to finish sampling
      ticks = READ_SYS_RTC_COUNT();
      while(!(IADC0->STATUS & (_IADC_STATUS_CONVERTING_MASK \
            | _IADC_STATUS_SINGLEFIFODV_MASK))){
          if(READ_SYS_RTC_COUNT() - ticks > 327) {
              DS_app_log_error("adc timeout \r\n");
              break;
          }
      }
      sample = IADC_readSingleResult(IADC0);
      vData[i] = sample.data;

      IADC_command(IADC0, iadcCmdStopSingle);
  }
  IADC_reset(IADC0);
}

uint16_t get_battery_monitor_voltage(void)
{
    uint16_t voltageMilliV;
    uint32_t sum = 0;
    uint8_t cnt = 0;
    for(uint8_t i=0;i<8;i++) {
        if(vData[i] != 0) {
            sum += vData[i];
            cnt ++;
        }
    }
    if(cnt > 0) {
        sum = sum/cnt;
    }
    voltageMilliV = 4*AdcToMilliV(sum);
    return voltageMilliV;
}

/********************HUB device iadc init**************************************/

static volatile uint32_t scanResult[4];  // Volts

void iadc_scan_start(void)
{
  IADC_command(IADC0, iadcCmdStartScan);
}

void iadc_scan_stop(void)
{
  CMU_ClockEnable(cmuClock_IADC0, true);
  IADC_reset(IADC0);
}

/**************************************************************************//**
 * @brief  IADC Initializer
 *****************************************************************************/
void initIADC(IADC_PosInput_t input0, IADC_PosInput_t input1)
{
  IADC_Init_t init = IADC_INIT_DEFAULT;
  IADC_AllConfigs_t initAllConfigs = IADC_ALLCONFIGS_DEFAULT;
  IADC_InitScan_t initScan = IADC_INITSCAN_DEFAULT;
  IADC_ScanTable_t initScanTable = IADC_SCANTABLE_DEFAULT;    // Scan Table

  CMU_ClockEnable(cmuClock_IADC0, true);
  CMU_ClockEnable(cmuClock_GPIO, true);

  // Select clock for IADC
  CMU_ClockSelectSet(cmuClock_IADCCLK, cmuSelect_FSRCO);

  // Modify init structures and initialize
  init.warmup = iadcWarmupKeepWarm;

  // Set the HFSCLK prescale value here
  init.srcClkPrescale = IADC_calcSrcClkPrescale(IADC0, CLK_SRC_ADC_FREQ, 0);

#ifdef SM_PIR
  initAllConfigs.configs[0].reference = iadcCfgReferenceVddx;
#else
  initAllConfigs.configs[0].reference = iadcCfgReferenceInt1V2;
#endif
  initAllConfigs.configs[0].vRef = REFERENCE_VOLTAGE;
  //initAllConfigs.configs[0].osrHighSpeed = iadcCfgOsrHighSpeed2x;
  //initAllConfigs.configs[0].analogGain = iadcCfgAnalogGain0P5x;

  // Divide CLK_SRC_ADC to set the CLK_ADC frequency
  initAllConfigs.configs[0].adcClkPrescale = IADC_calcAdcClkPrescale(IADC0,
                                             CLK_ADC_FREQ,
                                             0,
                                             iadcCfgModeNormal,
                                             init.srcClkPrescale);

  initScan.dataValidLevel = _IADC_SCANFIFOCFG_DVL_VALID3;

  initScan.showId = true;

  initScanTable.entries[0].posInput = input0;
  initScanTable.entries[0].negInput = iadcNegInputGnd;
  initScanTable.entries[0].includeInScan = true;

  initScanTable.entries[1].posInput = input1;
  initScanTable.entries[1].negInput = iadcNegInputGnd;
  initScanTable.entries[1].includeInScan = true;

  initScanTable.entries[2].posInput = iadcPosInputAvdd;
  initScanTable.entries[2].negInput = iadcNegInputGnd | 1;
  initScanTable.entries[2].includeInScan = true;

#if defined(SM_OUTSOUND)
  initScanTable.entries[3].posInput = iadcPosInputPortAPin0;
  initScanTable.entries[3].negInput = iadcNegInputGnd;
  initScanTable.entries[3].includeInScan = true;
#endif

  // Initialize IADC
  IADC_init(IADC0, &init, &initAllConfigs);

  // Initialize scan
  IADC_initScan(IADC0, &initScan, &initScanTable);

#ifdef HUB_DEVICE
  GPIO->ABUSALLOC |= GPIO_ABUSALLOC_AEVEN0_ADC0;
  GPIO->CDBUSALLOC |= GPIO_CDBUSALLOC_CDEVEN0_ADC0;
#endif
#if defined(SM_KP) || defined(SM_SOUND) || defined(SM_PIR)
  GPIO->CDBUSALLOC |= GPIO_CDBUSALLOC_CDEVEN0_ADC0;
  GPIO->CDBUSALLOC |= GPIO_CDBUSALLOC_CDODD0_ADC0;
#endif
#if defined(SM_OUTSOUND)
  GPIO->ABUSALLOC |= GPIO_ABUSALLOC_AEVEN0_ADC0;
  GPIO->ABUSALLOC |= GPIO_ABUSALLOC_AODD0_ADC0;
  GPIO->CDBUSALLOC |= GPIO_CDBUSALLOC_CDEVEN0_ADC0;
#endif
  IADC_clearInt(IADC0, _IADC_IF_MASK);

  IADC_enableInt(IADC0, IADC_IEN_SCANTABLEDONE);

  NVIC_ClearPendingIRQ(IADC_IRQn);
  NVIC_EnableIRQ(IADC_IRQn);

  iadc_scan_start();
}

/**************************************************************************//**
 * @brief  IADC interrupt handler
 *****************************************************************************/
void IADC_IRQHandler(void)
{
  IADC_Result_t result = {0, 0};
  while (IADC_getScanFifoCnt(IADC0))
  {
    result = IADC_pullScanFifoResult(IADC0);
    scanResult[result.id] = result.data;

    if(result.id == 2) {
        scanResult[result.id] = 4 * result.data;
    }
  }
  IADC_setScanMask(IADC0, 0x000F);
  IADC_clearInt(IADC0, IADC_IF_SCANTABLEDONE);
}

#ifdef SM_PIR

#define LETIMER_FREQ    10

void initLETIMER(void)
{
  CMU_LFXOInit_TypeDef lfxoInit = CMU_LFXOINIT_DEFAULT;
  LETIMER_Init_TypeDef letimerInit = LETIMER_INIT_DEFAULT;

  // Initialize the LFXO and use it as the EM23GRPACLK source
  CMU_LFXOInit(&lfxoInit);
  CMU_ClockSelectSet(cmuClock_EM23GRPACLK, cmuSelect_LFXO);

  CMU_ClockEnable(cmuClock_LETIMER0, true);

  // Calculate the top value (frequency) based on clock source
  uint32_t topValue = CMU_ClockFreqGet(cmuClock_LETIMER0) / LETIMER_FREQ;

  // Reload top on underflow, pulse output, and run in free mode
  letimerInit.comp0Top = true;
  letimerInit.topValue = topValue;
  letimerInit.ufoa0 = letimerUFOAPulse;
  letimerInit.repMode = letimerRepeatFree;

  // Initialize LETIMER
  LETIMER_Init(LETIMER0, &letimerInit);

  // Clear any previous interrupt flags
  LETIMER_IntClear(LETIMER0, _LETIMER_IF_MASK);

  // Enable underflow interrupts
  LETIMER_IntEnable(LETIMER0, LETIMER_IEN_UF);

  // Enable LETIMER interrupts
  NVIC_ClearPendingIRQ(LETIMER0_IRQn);
  NVIC_EnableIRQ(LETIMER0_IRQn);
}

void leTimerInterruptStop(void)
{
  CMU_ClockEnable(cmuClock_LETIMER0, true);
  LETIMER_IntDisable(LETIMER0, LETIMER_IEN_UF);
  LETIMER_Enable(LETIMER0, false);

}

/**************************************************************************//**
 * @brief  LETIMER IRQ Handler
 *****************************************************************************/
void LETIMER0_IRQHandler(void)
{
  uint32_t flags = LETIMER_IntGet(LETIMER0);

  // Trigger an IADC scan conversion
  IADC_command(IADC0, iadcCmdStartScan);

  // Clear LETIMER interrupt flags
  LETIMER_IntClear(LETIMER0, flags);
}

#endif

uint32_t get_hub_voltage(void)
{
  DS_app_log_info("scanResule[0] = %ld \r\n", scanResult[0]);
  uint32_t voltage = scanResult[0] * REFERENCE_VOLTAGE / 0xFFF;

  return voltage;
}

uint32_t get_hub_temperature(void)
{
  DS_app_log_info("scanResule[1] = %ld \r\n", scanResult[1]);
  uint32_t tempereture = scanResult[1] * REFERENCE_VOLTAGE / 0xFFF;
  return tempereture;
}

uint32_t get_adc_channel(uint8_t channel)
{
  uint32_t value = 0;
  if(channel < 4) {
      value = scanResult[channel] * REFERENCE_VOLTAGE / 0xFFF;
      DS_app_log_error("value[%d] = %ld \r\n", channel, value);
  }
  return value;
}

uint32_t get_avdd_vlotage(void)
{
  DS_app_log_info("scanResule[2] = %ld \r\n", scanResult[2]);
  uint32_t avdd = scanResult[2] * REFERENCE_VOLTAGE / 0xFFF;
  return avdd;
}

